﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace Fishpark.PdfWriter.Linearisation
{
    /// <summary>
    /// The CrossReferenceTables class represents the full set of cross-reference tables in a PDF document.
    /// It is used by the PdfLineariser class to maintain a set of all the tables in the source document.
    /// </summary>
    internal class CrossReferenceTables
    {
        // Private fields
        // ==============
        #region Private fields
        private List<CrossReferenceTable> _tables;
        private CrossReferenceTable _current;
        #endregion



        // Public properties
        // =================
        #region Public properties
        /// <summary>
        /// Retrieves the total number of entries across all tables.
        /// </summary>
        public int Count
        {
            get
            {
                int count = 0;
                foreach (CrossReferenceTable table in _tables) count += table.Length;

                return count;
            }
        }

        /// <summary>
        /// Retrieves the cross-reference entries across all the tables.
        /// </summary>
        public IEnumerable<CrossReferenceEntry> Entries
        {
            get
            {
                foreach (CrossReferenceTable table in _tables)
                    foreach (CrossReferenceEntry entry in table.Entries)
                        yield return entry;
            }
        }
        #endregion



        // Public methods
        // ==============
        #region Public methods
        /// <summary>
        /// Adds a new object reference to the currently opened cross-reference table.
        /// </summary>
        /// <param name="objRef">The object reference</param>
        /// <param name="location">The location within the stream of the object reference.</param>
        public void Add(PdfObjectReference objRef, long location)
        {
            Debug.Assert(null != _current);

            _current.Add(objRef, location);
        }

        /// <summary>
        /// Finds the index of the specified object within the total list of objects across all tables.
        /// </summary>
        /// <param name="objRef">The object reference to look for</param>
        /// <returns>The index across all tables, or -1 of the object could not be found.</returns>
        public int FindIndex(PdfObjectReference objRef)
        {
            // We can quickly find the index of an object by going through the tables. We know if a table contains an object
            // because the table entries are strictly ordered. For each table that doesn't contain the object, we add the table's
            // length to the index.
            // For the table that contains the object, we calculate the object's position within it, and add that to the index.

            int index = 0;
            bool found = false;
            foreach (CrossReferenceTable table in _tables)
            {
                if (table.Start <= objRef.Id && (table.Start + table.Length) > objRef.Id)
                {
                    index += objRef.Id - table.Start;
                    found = true;
                    break;
                }
                else index += table.Length;
            }
            if (false == found) index = -1;

            return index;
        } // FindIndex()

        /// <summary>
        /// Opens a new cross-reference table.
        /// </summary>
        /// <param name="start">The first object in this table</param>
        /// <param name="length">The number of objects in the table.</param>
        public void NewTable(int start, int length)
        {
            CrossReferenceTable table = new CrossReferenceTable(start, length);
            _current = table;
            _tables.Add(table);
        }
        #endregion



        // Public constructor
        // ==================
        #region Public constructor
        /// <summary>
        /// Constructs a new CrossReferenceTables instance.
        /// </summary>
        public CrossReferenceTables()
        {
            _tables = new List<CrossReferenceTable>();
        }
        #endregion

    } // CrossReferenceTables class
}
